import {CreateElement, Vue} from "vue/types/vue";
import {VNode} from "vue";
import {DynamicForm} from "./index.vue.d";
import {ConfigItemConfig, SelectObjectType} from "../select-object/ConfigType";
import {CascadeType} from "@/components/dynamic-form/CascadeUtils";

export class DefaultForm {
  static span = 6
  static gutter = 20
  static labelAppend = "："
  static showTitle = false;
}

export type formItemsFunc = (formData: object) => FormItemConfig[]

export class FormConfig {
  span = 6 // 全局宽度
  gutter = 20 // 距离
  inline = false // 行内
  labelPosition = 'right'
  labelWidth = '100px'
  events = {} // 记录事件
  showTitle = DefaultForm.showTitle // 表单是否显示title
  formItems: Array<FormItemConfig | formItemsFunc> = []

  constructor(self: FormConfig, formItems: Array<FormItemConfig | formItemsFunc>) {
    Object.keys(self).forEach(key => this[key] = self[key])
    this.formItems = formItems
  }
}

/**
 * 组件的props
 */
export interface ComProps {
  /**
   * 组件的子类型
   */
  type?: 'monthrange' | 'daterange'
    | 'datetimerange' | 'yearrange' | 'quarterrange'
    | 'textarea'
    | string;
  format?: string;
  'value-format'?: string;
  clearable?: boolean;
  disabled?: boolean;
  loading?: boolean;

  setDisabled?(formData: object, item: FormItemConfig): boolean;

  title?: string;

  setShowTitle?(formData: object, item: FormItemConfig): string;

  props?: {
    value?: string;
    label?: string;
    children?: any;
  };
  options?: Array<any>;

  setOptions?(): Array<any>;

  asyncSetOptions?(): Promise<Array<any>>;

  /***
   * 获取焦点重新加载
   */
  focusLoadAgain?(): boolean;

  setMarks?(): any;

  setTexts?(): any;

  setColors?(): any;

  setPredefine?(): any;

  propsMin?: object;
  propsMax?: object;
  eventsMin?: object;
  eventsMax?: object;

  /**
   * selectObject
   */
  selectMultiple?: boolean;
  /**
   * selectObject
   */
  showLoadAgain?: boolean;
  /**
   * selectObject
   */
  selectShowView?: SelectObjectType.selectShowView;
  /**
   * selectObject
   */
  formConfigProps?: FormConfig;
  /**
   * selectObject
   */
  config?: ConfigItemConfig[];
  /**
   * selectObject
   */
  formOtherItems?: FormItemConfig[];
  /**
   * selectObject
   */
  asyncSetTableData?: SelectObjectType.asyncSetTableData;

  [key: string]: any;
}

export interface FormItemConfig {
  labelWidth?: string;
  span?: number; // 宽度 默认为6
  label?: string; // 前面文字
  type?: 'cascader' |
    'checkbox' |
    'colorPicker' |
    'datePicker' |
    'input' |
    'inputNumber' |
    'inputRange' |
    'radio' | 'rate' |
    'select' | 'selectObject' |
    'slider' | 'switch' |
    'timePicker' | 'treeSelect' |
    'slot' | string; // 类型---input
  slotName?: string; // 插槽的名字 没有这个就是用prop作为名字
  prop?: string; // 字段
  props?: ComProps; // 组件的props
  hidden?: string; // 组件隐藏false
  cascade?: CascadeType; // 组件级联
  /**
   * 是否渲染
   * @param createElement
   * @param formItem
   * @param formData
   */
  noFormItem?(createElement: CreateElement, formItem: FormItemConfig, formData): VNode[];

  /**
   * 设置隐藏
   * @param formData
   * @param formItem
   */
  setHidden?(formData, formItem: FormItemConfig): boolean;

  initValue?: string; // 初始化值
  opacity?: boolean; // boolean 隐藏占位
  /**
   *  隐藏元素，但是有占位
   * @param formData
   * @param formItem
   */
  setOpacity?(formData, formItem: FormItemConfig): boolean;

  /**
   * model的单边实现
   * @param value
   */
  getModel?(value): any;

  /**
   * model的单边实现
   * @param value
   */
  setModel?(value): any;

  /**
   * label自顶渲染函数, 同时支持使用作用域插槽，名字为 属性prop + ‘Label’
   * @param h
   * @param props
   * @param item
   * @param labelAppend {string}
   * @return {VNode[]}
   */
  labelSlot?(h: CreateElement, props, item: FormItemConfig, labelAppend: string): VNode[];

  /**
   * 初始化插槽数据
   * @param formData {{}}
   * @param item
   */
  initSlotData?(formData, item: FormItemConfig): void;

  /**
   * 全自定义渲染自己， 同时支持使用作用域插槽，名字为 属性prop
   * @param h
   * @param item
   * @param formData {{}}
   * @return {VNode[]}
   */
  render?(h: CreateElement, item: FormItemConfig, formData): VNode[];

  /**
   * 全自定义渲染自己， 同时支持使用作用域插槽，名字为 prop+'NoFormItem' 外部不渲染el-form-item
   * @param h
   * @param item
   * @param formData {{}}
   * @return {VNode[]}
   */
  renderNoFormItem: (h: CreateElement, item: FormItemConfig, formData) => VNode[];

  events?: ComEvents;
  ref?: string // 组件的引用
  /**
   * 组件插槽
   * @param h
   * @param item
   * @param formData 组件维护的表单对象
   * @return {TypeSlots}
   */
  typeSlots?(h: CreateElement, item: FormItemConfig, formData): TypeSlots;

  /**
   * 校验规则
   */
  rules?: FormItemRule[];
}

export type FormItemRuleCallBack = (err?: Error) => void

export interface FormItemRule {
  required?: boolean;
  message?: string;
  trigger?: 'blur' | 'change';
  min?: number;
  max?: number;
  validator?: (rule: FormItemRule, value: any, callback: FormItemRuleCallBack, formData: object) => void
  // 增加一个同步的写法，避免每次少写了callback，判断是否有同步执行，有就直接执行，没有就执行原始校验方法
  validatorAsync?: (rule: FormItemRule, value: any, formData: object) => void | Error | Promise<string>;
}

export class TypeSlots {
  /**
   * 组件插槽渲染
   * @param props {?}
   * @return {VNode[]}
   */
  default(props): VNode[] {
    return []
  }

  [key: string]: (props) => VNode[]
}

export interface ComEvents {
  input?(val: number | string): void;

  change?(val): void;

  /**
   * 如果没有options则没有第一个参数
   * @param options
   * @param val
   */
  change?(options, val): void;

  /**
   * 子项的change事件
   * @param val
   * @param type
   * @param value
   */
  optionsChange?(val, type, value): void;

  /**
   * selectObject
   * @param val
   */
  confirm?(val: [] | {}): void;

  [key: string]: any;
}


export interface RenderTypeItem {
  [key: string]: any;

  name: string;
  components?: { [key: string]: Vue | Promise<Vue> }; // 定义用到的组件

  /**
   * 渲染函数
   * @param createElement
   * @param vm
   * @param formItem
   * @return {VNode[]}
   */
  render(createElement: CreateElement, vm: DynamicForm, formItem: FormItemConfig): VNode[];

  /**
   * 初始化自己的formItem配置
   * @param vm
   * @param formItem
   */
  initFormItem?(vm: DynamicForm, formItem: FormItemConfig): void

  /**
   * 初始化自己的绑定值
   * @param vm
   * @param formItem
   */
  initFormValue?(vm: DynamicForm, formItem: FormItemConfig): void

  /**
   * 显示的title
   * @param vm
   * @param formItem
   */
  getTitle?(vm: DynamicForm, formItem: FormItemConfig): string

  /**
   * 非作用于插槽名字
   */
  slotNames?: string[]
  /**
   * 作用于插槽名字
   */
  scopedSlotsNames?: string[]
}

export interface GlobelConfigType {
  externalComponents: RenderTypeItem[]
}

export const GlobelConfig: GlobelConfigType = {
  /**
   * 外部组件
   */
  externalComponents: []
}
